vim.g.mapleader = ' '
local keymap = vim.keymap

keymap.set('n', '<c-a>', 'ggVG')

keymap.set({ 'n', 'x' }, '<leader>p', '"0p')

keymap.set('n', '<leader>q', '<cmd>q<cr>')
keymap.set('n', '<leader>w', '<cmd>w<cr>')
keymap.set('n', '<leader>x', '<cmd>x<cr>')

keymap.set('n', 'j', [[v:count?'j':'gj']], { noremap = true, expr = true })
keymap.set('n', 'k', [[v:count?'k':'gk']], { noremap = true, expr = true })

local opt = {
  noremap = true,
  -- true 悬浮窗口显示, flase 不以悬浮窗口显示
  silent = false,
}
-- visual模式下缩进代码
keymap.set('v', '<', '<cmd><gv', opt)
keymap.set('v', '>', '<cmd>gv', opt)

-- 在visual mode 里粘贴不要复制
keymap.set('v', 'p', '"_dP', opt)

-- 上下移动选中文本
keymap.set('v', 'J', "<cmd>:move '>+1<CR>gv-gv", opt)
keymap.set('v', 'K', "<cmd>:move '<-2<CR>gv-gv", opt)

-- treesitter 折叠
keymap.set('n', 'zc', '<cmd>:foldclose<CR>', opt)
keymap.set('n', 'zo', '<cmd>:foldopen<CR>', opt)

-- 左右比例控制
keymap.set('n', '<C-Left>', ':vertical resize -2<CR>', opt)
keymap.set('n', '<C-Right>', ':vertical resize +2<CR>', opt)
keymap.set('n', 's,', ':vertical resize -20<CR>', opt)
keymap.set('n', 's.', ':vertical resize +20<CR>', opt)
-- 上下比例
keymap.set('n', 'sj', ':resize +10<CR>', opt)
keymap.set('n', 'sk', ':resize -10<CR>', opt)
keymap.set('n', '<C-Down>', ':resize +2<CR>', opt)
keymap.set('n', '<C-Up>', ':resize -2<CR>', opt)
-- 相等比例
keymap.set('n', 's=', '<C-w>=', opt)

-- 退出
keymap.set('n', 'q', '<cmd>:q<CR>', opt)
keymap.set('n', 'qq', '<cmd>:q!<CR>', opt)
keymap.set('n', '<leader>q', '<cmd>:qa!<CR>', opt)

--#region
--#region
--#region
--#region
key_map = {}

--#region
--telescope
key_map.telescope = function(builtin, actions)
  -- disable_devicons
  local defaults = { previewer = true, disable_devicons = true }
  local pickers = setmetatable({}, {
    __index = function(_, key)
      if builtin[key] == nil then
        error 'Invalid key, please check :h telescope.builtin'
        return
      end
      return function(opts)
        opts = vim.tbl_extend('keep', opts or {}, defaults)
        builtin[key](opts)
      end
    end,
  })
  -- pickers setting
  keymap.set('n', '<leader>f', pickers.find_files, {})
  keymap.set('n', '<leader>g', pickers.live_grep, {})
  keymap.set('n', '<leader>b', pickers.buffers, {})

  -- defaults key setting
  local tele_key_map = {
    i = {
      -- 上下移动
      ['<C-j>'] = actions.move_selection_next,
      ['<C-k>'] = actions.move_selection_previous,
      -- ['<C-n>'] = actions.move_selection_next,
      -- ['<C-p>'] = actions.move_selection_previous,
      -- 历史记录
      ['<Down>'] = actions.cycle_history_next,
      ['<Up>'] = actions.cycle_history_prev,
      -- 关闭窗口
      -- ["<esc>"] = actions.close,
      ['<C-c>'] = actions.close,
      -- 预览窗口上下滚动
      ['<C-u>'] = actions.preview_scrolling_up,
      ['<C-d>'] = actions.preview_scrolling_down,
    },
  }
  return tele_key_map
end
--#endregion

--#region
-- cmp keybind
key_map.cmp = function(cmp)
  local feedkey = function(key, mode)
    vim.api.nvim_feedkeys(
      vim.api.nvim_replace_termcodes(key, true, true, true),
      mode,
      true
    )
  end
  local has_words_before = function()
    local line, col = table.unpack(vim.api.nvim_win_get_cursor(0))
    return col ~= 0
      and vim.api
          .nvim_buf_get_lines(0, line - 1, line, true)[1]
          :sub(col, col)
          :match '%s'
        == nil
  end
  return {
    -- 上一个
    ['<C-k>'] = cmp.mapping.select_prev_item(),
    ['<Up>'] = cmp.mapping.select_prev_item {
      behavior = cmp.SelectBehavior.Select,
    },
    -- 下一个
    ['<C-j>'] = cmp.mapping.select_next_item(),
    ['<TAB>'] = cmp.mapping.select_next_item {
      behavior = cmp.SelectBehavior.Select,
    },
    -- 出现补全
    ['<A-.>'] = cmp.mapping(cmp.mapping.complete(), { 'i', 'c' }),
    -- 取消
    ['<A-,>'] = cmp.mapping {
      i = cmp.mapping.abort(),
      c = cmp.mapping.close(),
    },
    -- 确认
    -- Accept currently selected item. If none selected, `select` first item.
    -- Set `select` to `false` to only confirm explicitly selected items.
    ['<CR>'] = cmp.mapping.confirm {
      select = true,
      behavior = cmp.ConfirmBehavior.Replace,
    },
    -- ['<C-y>'] = cmp.config.disable, -- Specify `cmp.config.disable` if you want to remove the default `<C-y>` mapping.
    -- 如果窗口内容太多，可以滚动
    ['<C-u>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }),
    ['<C-d>'] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c' }),
    -- snippets 跳转
    ['<C-l>'] = cmp.mapping(function(_)
      if vim.fn['vsnip#available'](1) == 1 then
        feedkey('<Plug>(vsnip-expand-or-jump)', '')
      end
    end, { 'i', 's' }),
    -- Help
    ['<C-h>'] = cmp.mapping(function()
      if vim.fn['vsnip#jumpable'](-1) == 1 then
        feedkey('<Plug>(vsnip-jump-prev)', '')
      end
    end, { 'i', 's' }),
    -- super Tab
    ['<Tab>'] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_next_item()
      elseif vim.fn['vsnip#available'](1) == 1 then
        feedkey('<Plug>(vsnip-expand-or-jump)', '')
      elseif has_words_before() then
        cmp.complete()
      else
        fallback() -- The fallback function sends a already mapped key. In this case, it's probably `<Tab>`.
      end
    end, { 'i', 's' }),
    ['<S-Tab>'] = cmp.mapping(function()
      if cmp.visible() then
        cmp.select_prev_item()
      elseif vim.fn['vsnip#jumpable'](-1) == 1 then
        feedkey('<Plug>(vsnip-jump-prev)', '')
      end
    end, { 'i', 's' }),
    -- end of super Tab
  }
end
--#endregion

--#region
--lspsaga
key_map.lspsaga = function(builtin)
  -- builtin setting
  vim.api.nvim_create_autocmd('LspAttach', {
    group = vim.api.nvim_create_augroup('UserLspConfig', {}),
    callback = function(ev)
      -- Enable completion triggered by <c-x><c-o>
      vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc'

      local opts = { buffer = ev.buf }
      vim.keymap.set('n', 'gd', '<cmd>Lspsaga goto_definition<cr>', opts)
      vim.keymap.set('n', '<space>r', '<cmd>Lspsaga rename<cr>', opts)
      vim.keymap.set('n', 'gr', builtin.lsp_references, opts)
    end,
  })

  -- for crates.nvim
  local function show_documentation()
    local filetype = vim.bo.filetype
    if vim.tbl_contains({ 'vim', 'help' }, filetype) then
      vim.cmd('h ' .. vim.fn.expand '<cword>')
    elseif vim.tbl_contains({ 'man' }, filetype) then
      vim.cmd('Man ' .. vim.fn.expand '<cword>')
    elseif
      vim.fn.expand '%:t' == 'Cargo.toml'
      and require('crates').popup_available()
    then
      require('crates').show_popup()
    else
      vim.cmd 'Lspsaga hover_doc'
    end
  end

  -- key setting
  keymap.set('n', '<space>k', show_documentation, { silent = true })
  keymap.set('n', '[d', '<cmd>Lspsaga diagnostic_jump_prev<cr>')
  keymap.set('n', ']d', '<cmd>Lspsaga diagnostic_jump_next<cr>')
  keymap.set('n', '<leader>o', '<cmd>Lspsaga outline<cr>')
  keymap.set('n', '<leader>ca', '<cmd>Lspsaga code_action<CR>')
  keymap.set('v', '<leader>ca', '<cmd><C-U>Lspsaga range_code_action<CR>')
  -- Float terminal
  -- if you want to pass some cli command into a terminal you can do it like this
  -- open lazygit in lspsaga float terminal
  keymap.set(
    'n',
    '<A-d>',
    '<cmd>Lspsaga term_toggle lazygit<CR>',
    { silent = true }
  )
end
--#endregion

--#region
-- todo-comments
key_map.todo = function(todo)
  vim.keymap.set('n', ']t', function()
    todo.jump_next()
  end, { desc = 'Next todo comment' })

  vim.keymap.set('n', '[t', function()
    todo.jump_prev()
  end, { desc = 'Previous todo comment' })

  vim.keymap.set(
    'n',
    '<leader>t',
    '<cmd>TodoTelescope<cr>',
    { desc = 'Previous todo comment' }
  )
end
--#endregion

--#region
-- tree
key_map.tree = function(api)
  vim.g.loaded_netrw = 1
  vim.g.loaded_netrwPlugin = 1
  keymap.set('n', '<c-e>', api.tree.toggle)

  return function(bufnr)
    local function opts(desc)
      return {
        desc = 'nvim-tree: ' .. desc,
        buffer = bufnr,
        noremap = true,
        silent = true,
        nowait = true,
      }
    end
    -- default mappings
    api.config.mappings.default_on_attach(bufnr)
    -- custom mappings
    keymap.set('n', '<c-e>', api.tree.toggle, opts 'Toggle')
    keymap.set('n', '?', api.tree.toggle_help, opts 'Help')
  end
end
--#endregion

--#region
--Comment
key_map.comment = function()
  -- ## NORMAL mode
  -- `gcc` - Toggles the current line using linewise comment
  -- `gbc` - Toggles the current line using blockwise comment
  -- `[count]gcc` - Toggles the number of line given as a prefix-count using linewise
  -- `[count]gbc` - Toggles the number of line given as a prefix-count using blockwise
  -- `gc[count]{motion}` - (Op-pending) Toggles the region using linewise comment
  -- `gb[count]{motion}` - (Op-pending) Toggles the region using blockwise comment

  -- ## VISUAL mode
  -- `gc` - Toggles the region using linewise comment
  -- `gb` - Toggles the region using blockwise comment

  -- # Linewise
  -- `gcw` - Toggle from the current cursor position to the next word
  -- `gc$` - Toggle from the current cursor position to the end of line
  -- `gc}` - Toggle until the next blank line
  -- `gc5j` - Toggle 5 lines after the current cursor position
  -- `gc8k` - Toggle 8 lines before the current cursor position
  -- `gcip` - Toggle inside of paragraph
  -- `gca}` - Toggle around curly brackets

  -- # Blockwise
  -- `gb2}` - Toggle until the 2 next blank line
  -- `gbaf` - Toggle comment around a function (w/ LSP/treesitter support)
  -- `gbac` - Toggle comment around a class (w/ LSP/treesitter support)
end
--#endregion

--#region
--gitsign
key_map.gitsigns = function(bufnr, gs)
  local opts = { buffer = bufnr }
  keymap.set('n', ']c', function()
    if vim.wo.diff then
      return ']c'
    end
    vim.schedule(function()
      gs.next_hunk()
    end)
    return '<Ignore>'
  end, { expr = true, buffer = bufnr })

  keymap.set('n', '[c', function()
    if vim.wo.diff then
      return '[c'
    end
    vim.schedule(function()
      gs.prev_hunk()
    end)
    return '<Ignore>'
  end, { expr = true, buffer = bufnr })

  -- Actions
  keymap.set({ 'n', 'v' }, '<leader>hs', '<cmd>:Gitsigns stage_hunk<CR>', opts)
  keymap.set({ 'n', 'v' }, '<leader>hr', '<cmd>:Gitsigns reset_hunk<CR>', opts)
  keymap.set('n', '<leader>hS', gs.stage_buffer, opts)
  keymap.set('n', '<leader>hu', gs.undo_stage_hunk, opts)
  keymap.set('n', '<leader>hR', gs.reset_buffer, opts)
  keymap.set('n', '<leader>hp', gs.preview_hunk, opts)
  keymap.set('n', '<leader>hb', function()
    gs.blame_line { full = true }
  end, opts)
  keymap.set('n', '<leader>tb', gs.toggle_current_line_blame, opts)
  keymap.set('n', '<leader>hd', gs.diffthis, opts)
  keymap.set('n', '<leader>hD', function()
    gs.diffthis '~'
  end, opts)
  keymap.set('n', '<leader>td', gs.toggle_deleted, opts)

  -- Text object
  keymap.set({ 'o', 'x' }, 'ih', ':<C-U>Gitsigns select_hunk<CR>', opts)
end
--#endregion

--#region
-- dap 调试插件
key_map.debug = function()
  -- 开始
  keymap.set('n', '<leader>rd', '<cmd>:RustDebuggables<CR>', opt)
  -- 结束
  keymap.set(
    'n',
    '<leader>de',
    "<cmd>:lua require'dap'.close()<CR>"
      .. ":lua require'dap'.terminate()<CR>"
      .. ":lua require'dap.repl'.close()<CR>"
      .. ":lua require'dapui'.close()<CR>"
      .. ":lua require('dap').clear_breakpoints()<CR>"
      .. '<C-w>o<CR>',
    opt
  )
  -- 设置断点
  keymap.set(
    'n',
    '<leader>dbb',
    "<cmd>:lua require('dap').toggle_breakpoint()<CR>",
    opt
  )
  keymap.set(
    'n',
    '<leader>dcb',
    "<cmd>:lua require('dap').clear_breakpoints()<CR>",
    opt
  )
  -- 继续
  keymap.set('n', '<F5>', "<cmd>:lua require'dap'.continue()<CR>", opt)
  --  stepOver, stepOut, stepInto
  keymap.set('n', '<F6>', "<cmd>:lua require'dap'.step_over()<CR>", opt)
  keymap.set('n', '<F7>', "<cmd>:lua require'dap'.step_into()<CR>", opt)
  keymap.set('n', '<F8>', "<cmd>:lua require'dap'.step_out()<CR>", opt)
  -- 弹窗
  keymap.set('n', '<leader>dh', "<cmd>:lua require'dapui'.eval()<CR>", opt)

  -- 未使用
  -- map.set("n", "<leader>dB", "<cmd>lua require'dap'.set_breakpoint(vim.fn.input '[Condition] > ')<cr>", opt)
  -- map.set("n", "<leader>dr", "<cmd>lua require'dap'.repl.open()<cr>", opt)
  -- map.set("n", "<leader>dl", "<cmd>lua require'dap'.run_last()<cr>", opt)
  -- map.set("n", "<F10>", '<cmd>lua require"user.dap.dap-util".reload_continue()<CR>', opt)
  -- map.set("n", "<F4>", "<cmd>lua require'dap'.terminate()<cr>", opt)
  -- map.set("n", "K", "<cmd>lua require'dapui'.eval()<cr>", opt)
  -- map.set("n", "<leader>dt", "<cmd>lua require'dapui'.toggle()<cr>", opt)
end

-- vimspector 调试插件 与dap 类似
-- vimspector
key_map.debug_vimspector = function()
  -- 开始
  keymap.set('n', '<leader>dd', '<cmd>:call vimspector#Launch()<CR>', opt)
  -- 结束
  keymap.set('n', '<Leader>de', '<cmd>:call vimspector#Reset()<CR>', opt)
  -- 继续
  keymap.set('n', '<F5>', '<cmd>:call vimspector#Continue()<CR>', opt)
  -- 设置断点
  keymap.set(
    'n',
    '<Leader>dt',
    '<cmd>:call vimspector#ToggleBreakpoint()<CR>',
    opt
  )
  keymap.set(
    'n',
    '<Leader>dT',
    '<cmd>:call vimspector#ClearBreakpoints()<CR>',
    opt
  )
  --  stepOver, stepOut, stepInto
  keymap.set('n', '<F6>', '<Plug>VimspectorStepOver', opt)
  keymap.set('n', '<F7>', '<Plug>VimspectorStepInto', opt)
  keymap.set('n', '<F8>', '<Plug>VimspectorStepOut', opt)
end
--#endregion

-- return
return key_map
